﻿$import( "SmitearWebGL.Core.ResourceItem" );
$import( "SmitearWebGL.Events.Event" );
$import( "SmitearWebGL.Objects.Entity" );
$import( "SmitearWebGL.Objects.Camera" );
$import( "SmitearWebGL.Objects.Viewport" );
$import( "SmitearWebGL.Scene.QuadTree" );
$import( "SmitearWebGL.Core.Geometry.Rect" );
$import( "SmitearWebGL.Core.Render.RenderSystem" );
$import( "SmitearWebGL.Renderer.LPPRenderer" );
$import( "SmitearWebGL.Renderer.HDRRenderer" );

SmitearWebGL.Scene.Scene = SmitearWebGL.Core.ResourceItem.extend(
{
	/******** constructor ********/
	
	init : function( worldWidth, worldHeight, worldDepth, treeDepth, rendererType )
	{
		this._super();
		this._registClass( "SmitearWebGL.Scene.Scene" );
		
		/******** public property ********/
		
		/******** private property ********/
		
		this._displayObjects = null;
		this._renderObjects = null;
		this._lights = null;
		this._root = null;
		
		this._quadTree = null;
		
		this._renderSyatem = null;
		this._renderSession = null;
		this._renderer = null;
		this._interval = 0;
		this._geometryInfo = null;
		
		this._isRendering = false;
		this._ambient = $C( 1.0, 1.0, 1.0, 1.0 );
	}
	,
	
	/******** public method ********/
	
	setAmbient : function( ambient )
	{
		this._ambient = ambient;
	}
	,
	getAmbient : function()
	{
		return this._ambient;
	},
	getLights : function()
	{
		return this._lights;
	}
	,
	getRenderObjects : function()
	{
		return this._renderObjects;
	}
	,
	getDisplayObjects : function()
	{
		return this._displayObjects;
	}
	,
	getQuadTree : function()
	{
		return this._quadTree;
	}
	,
	getRenderSystem : function()
	{
		return this._renderSyatem;
	}
	,
	getRenderSession : function()
	{
		return this._renderSession;
	}
	,
	setRenderer : function( renderer )
	{
		this._renderer = renderer;
	}
	,
	getRenderer : function()
	{
		return this._renderer;
	}
	,
	render : function( usingCamera, frameRate, count )
	{
		if ( ! this._renderer ) return SmitearWebGL.Core.HResult.UNKNOW_RENDERER;
		if ( this._isRendering ) return SmitearWebGL.Core.HResult.SCENE_RENDERING;
		if ( usingCamera == undefined || usingCamera == null ) return SmitearWebGL.Core.HResult.CAMERA_NULL;
		if ( frameRate < 0 ) return SmitearWebGL.Core.HResult.FRAME_RATE_INVALID;
		
		this._renderSession.scene = this;
		this._renderSession.camera = usingCamera;
		this._renderSession.farClip = usingCamera.getFarClip();
		this._renderSession.nearClip = usingCamera.getNearClip();
		this._renderSession.numOfFrames = count == undefined ? -1 : count;
		this._renderSyatem.startTimer();
		
		var self = this;
		
		this._interval = setInterval( function(){
			self.onRendering();
		},
		frameRate == undefined ? 1000 / 60 : 1000 / frameRate );
		
		this._isRendering = true;
		
		return SmitearWebGL.Core.HResult.SUCCESS;
	}
	,
	onRendering : function()
	{
		this._renderSyatem.updateTimer();
		this._renderSession.deltaTime = this._renderSyatem.deltaTime;
		
		this.dispatchEvent( SmitearWebGL.Scene.Scene._frameMoveEvent );
		
		//////////////////////////////// before render ////////////////////////////////
		
		this._renderSession.camera.updateTransform();
		
		for ( var i = 0; i < this._displayObjects.length; ++ i )
		{
			this._displayObjects[i].updateTransform();
			this._displayObjects[i].updateBeforeRender();
		}
		
		this._quadTree.updateBeforeRender();
		this._quadTree.buildSearchResults( this._renderSession.camera.getFrustum().frustumRect,
										 this._renderSession.camera.getNearClip(),
										 this._renderSession.camera.getFarClip() );
		
		//////////////////////////////// render ////////////////////////////////
		
		this._renderer.render( this );
		
		if ( SmitearWebGL.Scene._hdrEnable )
		{
			this._hdrRenderer.setSourceTexture( this._renderer._geomFBO.getRenderTarget( SmitearWebGL.Renderer.LPPRenderer.RTUsage.FBO_MATERIAL_TEX ) );
			this._hdrRenderer.render( this );
		}
		
		//////////////////////////////// after render ////////////////////////////////
		
		for ( i = 0; i < this._displayObjects.length; ++ i )
		{
			this._displayObjects[i].updateAfterRender();
		}
		
		this._renderSession.camera.updateAfterRender();
		
		this._quadTree.updateAfterRender();
		
		this.dispatchEvent( SmitearWebGL.Scene.Scene._frameRenderEvent );
		
		if ( ++ this._renderSession.frames == this._renderSession.numOfFrames )
		{
			this.stopRendering();
			
			return SmitearWebGL.Core.HResult.FRAMES_LIMIT;
		}
		
		return SmitearWebGL.Core.HResult.SUCCESS;
	}
	,
	stopRendering : function()
	{
		if ( ! this._isRendering ) return;
		
		this._isRendering = false;
		
		clearInterval( this._interval );
		this._interval = null;
	}
	,
	getChildAt : function( index )
	{
		return this._root.getChildAt( index );
	}
	,
	getChildByName : function( name )
	{
		return this._root.getChildByName( name );
	}
	,
	addChild : function( child )
	{
		return this._root.addChild( child );
	}
	,
	removeChild : function( child )
	{
		return this._root.removeChild( child );
	}
	,
	removeChildByName : function( name )
	{
		return this._root.removeChildByName( name );
	}
	,
	removeChildAt : function( index )
	{
		return this._root.removeChildAt( index );
	}
	,
	
	/******** private method ********/
	
	_createResource : function( params )
	{
		this._super();
		
		this._displayObjects = [];
		this._renderObjects = [];
		this._lights = [];
		this._root = this.context.objectManager.createResource( "__scene_root__", SmitearWebGL.Objects.Entity );
		this._displayObjects.push( this._root );
		this._root._scene = this;
		
		var treeDepth = params.treeDepth == undefined ? 2 : Math.floor( params.treeDepth );
		var halfWidth = params.worldWidth == undefined ? 250 : params.worldWidth / 2.0;
		var halfHeight = params.worldHeight == undefined ? 225 : params.worldHeight / 2.0;
		var halfLength = params.worldDepth == undefined ? 250 : params.worldDepth / 2.0;
		this._quadTree = new SmitearWebGL.Scene.QuadTree();
		this._quadTree.create( new SmitearWebGL.Core.Geometry.Rect( -halfWidth, halfWidth,
																	-halfHeight, halfHeight,
																	-halfLength, halfLength ),
																	treeDepth );
		
		this._renderer = this.context.rendererManager.getResourceByName( SmitearWebGL.Renderer.Renderer.Type.LIGHT_PRE_PASS_RENDERER );
		this._hdrRenderer = this.context.rendererManager.getResourceByName( SmitearWebGL.Renderer.Renderer.Type.HDR_RENDERER );
		
		this._renderSyatem = new SmitearWebGL.Core.Render.RenderSystem();
		this._renderSession = new SmitearWebGL.Core.Render.RenderSession();
		
		this._interval = 0;
		this._geometryInfo = null;
		
		this._isRendering = false;
	}
	,
	_destroyResource : function()
	{
		this._super();
		
		this.stopRendering();
		
		this._root.release();
		this._quadTree.release();
		this._renderer.release();
		this._hdrRenderer.release();
		
		this._displayObjects = null;
		this._renderObjects = null;
		this._lights = null;
		this._root = null;
		this._root._scene = null;
		this._quadTree = null;
		this._renderSyatem = null;
		this._renderSession = null;
		this._renderer = null;
		this._interval = 0;
		this._geometryInfo = null;
		this._isRendering = false;
	}
}
);

SmitearWebGL.Scene.Scene._frameMoveEvent = new SmitearWebGL.Events.Event( SmitearWebGL.Events.Event.FRAME_MOVE );
SmitearWebGL.Scene.Scene._frameRenderEvent = new SmitearWebGL.Events.Event( SmitearWebGL.Events.Event.FRAME_RENDER );

SmitearWebGL.Scene._ambient = $C( 1.0, 1.0, 1.0, 1.0 );
SmitearWebGL.Scene._shadowEnable = false;
SmitearWebGL.Scene._showShadowVolume = false;
SmitearWebGL.Scene._ssaoEnable = false;
SmitearWebGL.Scene._hdrEnable = false;

//开启/关闭阴影体
SmitearWebGL.Scene.enableShadow = function()
{
	SmitearWebGL.Scene._shadowEnable = true;
}

SmitearWebGL.Scene.disableShadow = function()
{
	SmitearWebGL.Scene._shadowEnable = false;
}

//显示/隐藏阴影体
SmitearWebGL.Scene.showShadowVolume = function()
{
	SmitearWebGL.Scene._showShadowVolume = true;
}

SmitearWebGL.Scene.hideShadowVolume = function()
{
	SmitearWebGL.Scene._showShadowVolume = false;
}

//开启/关闭屏幕空间环境光遮蔽
SmitearWebGL.Scene.enableSSAO = function()
{
	SmitearWebGL.Scene._ssaoEnable = true;
}

SmitearWebGL.Scene.disableSSAO = function()
{
	SmitearWebGL.Scene._ssaoEnable = false;
}

//环境光遮蔽参数
SmitearWebGL.Scene.setSSAOParam = function( strength, scale, falloff, rad )
{
	SmitearWebGL.Programs.LightPrePass.SSAOProgram.strength = strength;
	SmitearWebGL.Programs.LightPrePass.SSAOProgram.scale = scale;
	SmitearWebGL.Programs.LightPrePass.SSAOProgram.falloff = falloff;
	SmitearWebGL.Programs.LightPrePass.SSAOProgram.rad = rad;
}

SmitearWebGL.Scene.getSSAOParam = function()
{
	return { strength : SmitearWebGL.Programs.LightPrePass.SSAOProgram.strength,
			scale : SmitearWebGL.Programs.LightPrePass.SSAOProgram.scale,
			falloff : SmitearWebGL.Programs.LightPrePass.SSAOProgram.falloff,
			rad : SmitearWebGL.Programs.LightPrePass.SSAOProgram.rad }
}

//开启/关闭HDR
SmitearWebGL.Scene.enableHDR = function()
{
	SmitearWebGL.Scene._hdrEnable = true;
}

SmitearWebGL.Scene.disableHDR = function()
{
	SmitearWebGL.Scene._hdrEnable = false;
}

//HDR参数
SmitearWebGL.Scene.setHDRParam = function( threshold, gaussMultiplier, gaussMean, gaussStdDev, exposure )
{
	SmitearWebGL.Objects.Canvas.Hdr.Bloom.brightThreshold = threshold;
	SmitearWebGL.Objects.Canvas.Hdr.Blur.GaussMultiplier = gaussMultiplier;
	SmitearWebGL.Objects.Canvas.Hdr.Blur.GaussMean = gaussMean;
	SmitearWebGL.Objects.Canvas.Hdr.Blur.GaussStdDev = gaussStdDev;
	SmitearWebGL.Objects.Canvas.Hdr.Combine.Exposure = exposure;
}

SmitearWebGL.Scene.getHDRParam = function()
{
	return { threshold : SmitearWebGL.Objects.Canvas.Hdr.Bloom.brightThreshold,
			gaussMultiplier : SmitearWebGL.Objects.Canvas.Hdr.Blur.GaussMultiplier,
			gaussMean : SmitearWebGL.Objects.Canvas.Hdr.Blur.GaussMean,
			gaussStdDev : SmitearWebGL.Objects.Canvas.Hdr.Blur.GaussStdDev,
			exposure : SmitearWebGL.Objects.Canvas.Hdr.Combine.Exposure }
}